-
Notifications
You must be signed in to change notification settings - Fork 418
Update fee and dust handling for zero fee channels #3884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
👋 Thanks for assigning @tankyleo as a reviewer! |
This still needs a few tests, opening up early for conceptual review. |
@@ -5078,6 +5092,7 @@ where | |||
) -> u64 { | |||
if funding.get_channel_type().supports_anchor_zero_fee_commitments() { | |||
debug_assert_eq!(self.feerate_per_kw, 0); | |||
debug_assert!(fee_spike_buffer_htlc.is_none()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering whether it's worth having this assertion (which makes it necessary to check channel type before calling fee functions). The alternative would be to just ignore the parameters completely for zero fee channels (even though Some
fee_spike_buffer_htlc
doesn't make sense for the type).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't say I have a strong preference. The assertion seems fine to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All LGTM, didn't carefully check every hunk, though.
lightning/src/ln/channel.rs
Outdated
@@ -133,6 +134,22 @@ enum FeeUpdateState { | |||
Outbound, | |||
} | |||
|
|||
/// Returns the fees for success and timeout second stage HTLC transactions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Probably belongs in chan_utils.rs
given it kinda mirrors commit_and_htlc_tx_fees_sat
and is also called from there?
@@ -5078,6 +5092,7 @@ where | |||
) -> u64 { | |||
if funding.get_channel_type().supports_anchor_zero_fee_commitments() { | |||
debug_assert_eq!(self.feerate_per_kw, 0); | |||
debug_assert!(fee_spike_buffer_htlc.is_none()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't say I have a strong preference. The assertion seems fine to me.
001c8e9
to
4157d24
Compare
This fee rate is currently used in two scenarios: - To count any fees above what we consider to be a sane estimate towards our dust exposure. - To get a maximum dust exposure (when using MaxDustHTLCExposure::FeeEstimator strategy). When we have zero fee commitments: - Commitments are zero fee, so we don't need to count fees towards dust exposure. - The amount of dust we have is not dependent on fees, as everything is zero fee. - We still want to limit our total dust exposure. This commit updates get_dust_exposure_limiting_feerate to allow a None value to prepare for support for zero fee commitments. This clearly allows us to indicate when we don't care about fee rates for dust considerations. In get_max_dust_htlc_exposure_msat, we simply hardcode a value of 1 sat/vbyte if a feerate dependent strategy is being used.
Co-authored-by: Matt Corallo <[email protected]>
LDK does not support a channel type that supports both zero fee and nonzero fee anchors (as this is impossible), so we can drop the unnecessary check in build_htlc_output.
This commit pulls calculation of second stage fees into a helper function. A side effect of this refactor is that it fixes a rounding issue in commit_and_htlc_tx_fees_sat. Previously, rounding down of feerate would happen after multiplying by the number of HTLCs. Now the feerate will be rounded down before multiplying by the number of HTLCs. This wasn't a serious issue - it would just cause us very slightly over estimate our dust exposure at certain feerates that needed rounding. A hard-coded value in test_nondust_htlc_excess_fees_are_dust is updated to account for this rounding change.
Update second_stage_tx_fees_sat to return zero for zero fee commitments. As is the case for anchors_zero_fee_commitments, this changes ensures that we won't trim the second stage transaction fee off of the HTLC amount.
When we have zero fee commitments, we don't need to calculate our fee rate or check that it isn't stale because it is always zero. Co-authored-by: Matt Corallo <[email protected]>
Co-authored-by: Matt Corallo <[email protected]>
Update test helper in preparation to test zero fee commitments, where the local balance will differ from the previously hardcoded value.
4157d24
to
5fe6fa7
Compare
🔔 1st Reminder Hey @tankyleo! This PR has been waiting for your review. |
@@ -4775,7 +4779,8 @@ where | |||
let excess_feerate_opt = outbound_feerate_update |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth debug asserting that this is always 0 for zero-fee commitments ?
#[rustfmt::skip] | ||
pub(crate) fn htlc_tx_fees_sat(feerate_per_kw: u32, num_accepted_htlcs: usize, num_offered_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 { | ||
let htlc_tx_fees_sat = if !channel_type_features.supports_anchors_zero_fee_htlc_tx() { | ||
num_accepted_htlcs as u64 * htlc_success_tx_weight(channel_type_features) * feerate_per_kw as u64 / 1000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the commit message, I wouldn't say we round a feerate, but rather round a raw transaction fee amount.
let (htlc_success_tx_fee_sat, htlc_timeout_tx_fee_sat) = | ||
second_stage_tx_fees_sat(features, feerate_per_kw); | ||
|
||
let htlc_tx_fee_sat = if local { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: keep the formatting consistent between the two is_dust
functions ?
// Sender should not queue an update_fee message. | ||
nodes[0].node.timer_tick_occurred(); | ||
let events_0 = nodes[0].node.get_and_clear_pending_msg_events(); | ||
assert_eq!(events_0.len(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't expect an update_fee
to be generated in any channel type here.
This test has this log here:
node 0 TRACE [lightning::ln::channelmanager:6975] Channel 5109930e509248b4fdac5f8fef66915f693393cdbfd8f0cd2d56f0f4d80baf62 qualifies for a feerate change from 0 to 0.
Looks like we should update the condition to skip a fee update in fn update_channel_fee
:
if new_feerate <= chan.context.get_feerate_sat_per_1000_weight() && new_feerate * 2 > chan.context.get_feerate_sat_per_1000_weight() {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we should update the condition to skip a fee update in fn update_channel_fee:
I don't really feel like it's channel manger's responsibility to know that we shouldn't be sending fee updates for zero fee commitments (belongs in channel IMO), but checking "has this fee changed" is fair game so will look at updating 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right seems the condition is true for all cases where the old feerate is equal to the new feerate, except when that feerate is 0 :)
// side, only on the sender's. Note that with anchor outputs we are no longer as | ||
// sensitive to fee spikes, so we need to account for them. | ||
// `Some(())` is for the fee spike buffer we keep for the remote if the channel is | ||
// not zero fee.. This deviates from the spec because the fee spike buffer requirement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: delete the two dots
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still grokking some of the changes here and the zero-fee commitment proposal but from my kind of limited context it LGTM (:
This PR completes the off-chain handling of V3 channels, as described in #3789.